const db = require("../models");
const DAO = require("../dao/DAO");
const chalk = require('chalk');
const crypto = require('crypto');
const logger = require('../utils/_logger');
const Files = db.models.files;
const dayjs = require('dayjs');
const multer = require('multer');
const PATH = require('path');
const fs = require('fs');
const utils = require("../utils");
let uploadDir = multer({ dest: 'uploads_files' }).single('file') //存放文件夹

// size：初始文件大小
function getfilesize(size) {
    if (!size)
        return "";

    var num = 1024.00; //byte

    if (size < num)
        return size + "B";
    if (size < Math.pow(num, 2))
        return (size / num).toFixed(2) + "K"; //kb
    if (size < Math.pow(num, 3))
        return (size / Math.pow(num, 2)).toFixed(2) + "M"; //M
    if (size < Math.pow(num, 4))
        return (size / Math.pow(num, 3)).toFixed(2) + "G"; //G
    return (size / Math.pow(num, 4)).toFixed(2) + "T"; //T
}

function walkSync(currentDirPath, callback) {
    fs.readdirSync(currentDirPath, { withFileTypes: true }).forEach(function (dirent) {
        var filePath = PATH.join(currentDirPath, dirent.name);
        if (dirent.isFile()) {
            callback(filePath, dirent);
        } else if (dirent.isDirectory()) {
            walkSync(filePath, callback);
        }
    });
}

// 计算文件的哈希值
function calculateFileHash(filePath) {
    return new Promise((resolve, reject) => {
        const hash = crypto.createHash('sha256');
        const stream = fs.createReadStream(filePath);

        stream.on('data', (data) => hash.update(data));
        stream.on('end', () => resolve(hash.digest('hex')));
        stream.on('error', reject);
    });
}

// 上传文件
exports.create = (req, res) => {
    uploadDir(req, res, async function (err) {
        if (err instanceof multer.MulterError) {
            // 发生错误
            logger.errLog(err);
            console.log(chalk.bold.green(`multer.MulterError: ${err}`));
        } else if (err) {
            // 发生错误
            logger.errLog(err);
            console.log(chalk.bold.green(`uploadDir: ${err}`));
        }
        const pm = req.file;
        if (!pm) return res.status(500).json({ message: '上传失败' })
        // 计算 文件哈希值
        const hash = await calculateFileHash(pm.path);
        const _file = await Files.findOne({ where: { hash, deleted_at: null }, attributes: { exclude: ['hash', 'deletedAt', 'user_id'] } });
        if (_file) {
            // 删除临时文件
            fs.unlinkSync(pm.path);
            return res.status(201).json({ data: _file, message: '上传成功', meta: { "msg": "上传成功", "status": 0 } })
        }
        const user_id = req.user.id; // 用户ID
        const ext = pm.originalname.split(".").pop(); // 文件后缀
        const size = getfilesize(pm.size); // 文件大小
        // 按文件类型创建文件夹
        const targetPath = `${ext}/${dayjs().format("YYYY-MM-DD")}`;
        if (!fs.existsSync(`uploads_files/${targetPath}`)) {
            // 检查父级目录是否存在，如果不存在则先创建父级目录
            const parentDir = PATH.dirname(`uploads_files/${targetPath}`);
            if (!fs.existsSync(parentDir)) {
                fs.mkdirSync(parentDir, { recursive: true });
            }
            fs.mkdirSync(`uploads_files/${targetPath}`);
        }
        // 开启事务
        const t = await db.sequelize.transaction();
        const name = utils.tools.uuid();
        const newFile = {
            path: `/${targetPath}/${name}.${ext}`,
            name: pm.originalname,
            user_id,
            size,
            ext,
            mimetype: pm.mimetype,
            hash,
        }
        try {
            const file = await Files.create(newFile, { transaction: t });
            // 将文件保存在 目标文件夹下
            fs.renameSync(pm.path, `uploads_files/${targetPath}/${name}.${ext}`);
            await t.commit();
            const {user_id,hash,...data} = file.toJSON();
            res.status(201).json({ data, message: '上传成功', meta: { "msg": "上传成功", "status": 0 } })
        } catch (err) {
            logger.errLog(err);
            t.rollback();
            res.status(500).json({ message: '上传失败' })
        }
    })
};

exports.findAll = (req, res) => {
    const pm = {
        page_size: req.body?.page_size ?? 10,
        current_page: req.body?.current_page || 1,
        params: {
            name: req.body.name,
            created_at: req.body?.created_at ? { [db.Op.between]: req.body?.created_at ?? '' } : '',
        },
        attributes: { exclude: ['hash'] },
        include: [{
            model: db.models.users,
            attributes: ["id", "user_name"],
            where: req.body?.user_name ? { user_name: { [db.Op.substring]: req.body?.user_name ?? '' } } : {},
        }],
    }
    DAO.list(Files, pm, list => {
        res.sendResult(list)
    })
};

exports.findOne = (req, res, next) => {
    let name = req.query.id;
    name = decodeURI(name).replace('/static', 'uploads_files')
    let url = PATH.resolve(process.cwd(), name)
    //判断该文件是否存在
    fs.access(url, function (err) {
        if (!err) {
            res.set({
                "Content-type": "application/octet-stream",
                "Content-Disposition": "attachment;filename=" + encodeURI(name),
                'Content-Length': fs.statSync(url).size
            });
            let fReadStream = fs.createReadStream(url);
            fReadStream.pipe(res)
        } else {
            console.log(chalk.bold.red(`【文件不存在】 ${name} *** 文件不存在 地址：${req.method} ${req.baseUrl + req.path}`))
            res.status(605).sendResult({ data: {}, code: 605, message: '文件不存在' })
        }
    })
};

exports.delete = async (req, res) => {
    const pm = req.body;
    // 请求验证
    if (!pm.id) return res.sendResult({ data: '', code: -1, message: "ID不能为空！" });
    const list = await Files.findAll({ where: { id: pm.id }, raw: true, attributes: ['id', 'path'] });
    list.forEach(el => {
        const filePath = PATH.resolve(process.cwd(), `uploads_files/${el.path}`)
        fs.unlinkSync(filePath)
    })
    DAO.delete(Files, { id: pm.id }, data => {
        res.sendResult(data)
    })
};

// 预览
exports.preview = async (req, res) => {
    const { id, path } = req.query;
    if (!id && !path) return res.status(500).sendResultAto(null, -1, '当前网络不稳定，请稍后重试')
    let file = null;
    if (id) {
        file = await Files.findByPk(id, { attributes: ['path', 'mimetype', 'size'], raw: true });
    } else if (path) {
        file = await Files.findOne({ where: { path }, attributes: ['path', 'mimetype', 'size'], raw: true });
    }
    if (!file) return res.status(404).sendResultAto(null, -1, '当前网络不稳定，请稍后重试');
    // 设置请求头
    const filePath = PATH.resolve(process.cwd(), `uploads_files/${file.path}`)
    res.set({
        "Content-type": file.mimetype,
        'Content-Length': file.size
    });
    res.sendFile(filePath);
};
// 下载
exports.download = async (req, res, next) => {
    const { id, path } = req.query;
    if (!id && !path) return res.status(500).sendResultAto(null, -1, '当前网络不稳定，请稍后重试')
    let file = null;
    if (id) {
        file = await Files.findByPk(id, { attributes: ['path', 'name'], raw: true });
    } else if (path) {
        file = await Files.findOne({ where: { path } });
    }
    if (!file) return res.status(404).sendResultAto(null, -1, '当前网络不稳定，请稍后重试')
    const url = PATH.resolve(process.cwd(), `uploads_files/${file.path}`)
    fs.access(url, function (err) {
        if (err) {
            return res.status(404).sendResultAto(null, -1, '当前网络不稳定，请稍后重试')
        } else {
            res.set({
                "Content-type": "application/octet-stream",
                "Content-Disposition": "attachment;filename=" + encodeURI(file.name),
                'Content-Length': fs.statSync(url).size
            });
            let fReadStream = fs.createReadStream(url);
            fReadStream.pipe(res)
        }
    })
};
